home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / uucp-104.lha / uucp-1.04 / chat.c < prev    next >
C/C++ Source or Header  |  1993-02-13  |  32KB  |  1,430 lines

  1. /* chat.c
  2.    Chat routine for the UUCP package.
  3.  
  4.    Copyright (C) 1991, 1992 Ian Lance Taylor
  5.  
  6.    This file is part of the Taylor UUCP package.
  7.  
  8.    This program is free software; you can redistribute it and/or
  9.    modify it under the terms of the GNU General Public License as
  10.    published by the Free Software Foundation; either version 2 of the
  11.    License, or (at your option) any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful, but
  14.    WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.    General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22.    The author of the program may be contacted at ian@airs.com or
  23.    c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
  24.    */
  25.  
  26. #include "uucp.h"
  27.  
  28. #if USE_RCS_ID
  29. const char chat_rcsid[] = "$Id: chat.c,v 1.38 1992/10/21 03:44:46 ian Rel $";
  30. #endif
  31.  
  32. #include <ctype.h>
  33. #include <errno.h>
  34.  
  35. #include "uudefs.h"
  36. #include "uuconf.h"
  37. #include "conn.h"
  38. #include "prot.h"
  39. #include "system.h"
  40.  
  41. /* Local functions.  */
  42.  
  43. static int icexpect P((struct sconnection *qconn, int cstrings,
  44.                char **azstrings, size_t *aclens,
  45.                int ctimeout, boolean fstrip));
  46. static boolean fcsend P((struct sconnection *qconn, pointer puuconf,
  47.              const char *zsend,
  48.              const struct uuconf_system *qsys,
  49.              const struct uuconf_dialer *qdial,
  50.              const char *zphone,
  51.              boolean ftranslate, boolean fstrip));
  52. static boolean fcecho_send_strip P((struct sconnection *qconn,
  53.                     const char *z, size_t clen));
  54. static boolean fcecho_send_nostrip P((struct sconnection  *qconn,
  55.                       const char *z, size_t clen));
  56. static boolean fcecho_send P((struct sconnection *qconn, const char *z,
  57.                   size_t clen, boolean fstrip));
  58. static boolean fcphone P((struct sconnection *qconn,
  59.               pointer puuconf,
  60.               const struct uuconf_dialer *qdial,
  61.               const char *zphone,
  62.               boolean (*pfwrite) P((struct sconnection *qc,
  63.                         const char *zwrite,
  64.                         size_t cwrite)),
  65.               boolean ftranslate, boolean *pfquote));
  66. static boolean fctranslate P((pointer puuconf, const char *zphone,
  67.                   const char **pzprefix,
  68.                   const char **pzsuffix));
  69. static boolean fcprogram P((struct sconnection *qconn, pointer puuconf,
  70.                 char **pzprogram,
  71.                 const struct uuconf_system *qsys,
  72.                 const struct uuconf_dialer *qdial,
  73.                 const char *zphone, const char *zport,
  74.                 long ibaud));
  75.  
  76. /* Run a chat script with the other system.  The chat script is a
  77.    series of expect send pairs.  We wait for the expect string to show
  78.    up, and then we send the send string.  The chat string for a system
  79.    holds the expect and send strings separated by a single space.  */
  80.  
  81. boolean
  82. fchat (qconn, puuconf, qchat, qsys, qdial, zphone, ftranslate, zport, ibaud)
  83.      struct sconnection *qconn;
  84.      pointer puuconf;
  85.      const struct uuconf_chat *qchat;
  86.      const struct uuconf_system *qsys;
  87.      const struct uuconf_dialer *qdial;
  88.      const char *zphone;
  89.      boolean ftranslate;
  90.      const char *zport;
  91.      long ibaud;
  92. {
  93.   int cstrings;
  94.   char **azstrings;
  95.   size_t *aclens;
  96.   char **pzchat;
  97.   char *zbuf;
  98.   size_t cbuflen;
  99.   boolean fret;
  100.   int i;
  101.  
  102.   /* First run the program, if any.  */
  103.   if (qchat->uuconf_pzprogram != NULL)
  104.     {
  105.       if (! fcprogram (qconn, puuconf, qchat->uuconf_pzprogram, qsys, qdial,
  106.                zphone, zport, ibaud))
  107.     return FALSE;
  108.     }
  109.  
  110.   /* If there's no chat script, we're done.  */
  111.   if (qchat->uuconf_pzchat == NULL)
  112.     return TRUE;
  113.  
  114.   if (qchat->uuconf_pzfail == NULL)
  115.     {
  116.       cstrings = 1;
  117.       azstrings = (char **) xmalloc (sizeof (char *));
  118.       aclens = (size_t *) xmalloc (sizeof (size_t));
  119.     }
  120.   else
  121.     {
  122.       char **pz;
  123.  
  124.       /* We leave string number 0 for the chat script.  */
  125.       cstrings = 1;
  126.       for (pz = qchat->uuconf_pzfail; *pz != NULL; pz++)
  127.     ++cstrings;
  128.  
  129.       azstrings = (char **) xmalloc (cstrings * sizeof (char *));
  130.       aclens = (size_t *) xmalloc (cstrings * sizeof (size_t));
  131.  
  132.       /* Get the strings into the array, and handle all the escape
  133.      characters.  */
  134.       for (cstrings = 1, pz = qchat->uuconf_pzfail;
  135.        *pz != NULL;
  136.        cstrings++, pz++)
  137.     {
  138.       azstrings[cstrings] = zbufcpy (*pz);
  139.       aclens[cstrings] = cescape (azstrings[cstrings]);
  140.     }
  141.     }
  142.  
  143.   cbuflen = 0;
  144.   zbuf = NULL;
  145.   fret = TRUE;
  146.  
  147.   pzchat = qchat->uuconf_pzchat;
  148.  
  149.   while (*pzchat != NULL)
  150.     {
  151.       size_t clen;
  152.  
  153.       /* Loop over subexpects and subsends.  */
  154.       while (TRUE)
  155.     {
  156.       /* Copy the expect string into the buffer so that we can
  157.          modify it in cescape.  */
  158.       clen = strlen (*pzchat);
  159.       if (clen >= cbuflen)
  160.         {
  161.           ubuffree (zbuf);
  162.           zbuf = zbufalc (clen + 1);
  163.           cbuflen = clen;
  164.         }
  165.       memcpy (zbuf, *pzchat, clen + 1);
  166.  
  167.       azstrings[0] = zbuf;
  168.       if (azstrings[0][0] == '-')
  169.         ++azstrings[0];
  170.       aclens[0] = cescape (azstrings[0]);
  171.  
  172.       if (aclens[0] == 0
  173.           || (aclens[0] == 2
  174.           && strcmp (azstrings[0], "\"\"") == 0))
  175.         {
  176.           /* There is no subexpect sequence.  If there is a
  177.          subsend sequence we move on to it.  Otherwise we let
  178.          this expect succeed.  This is somewhat inconsistent,
  179.          but it seems to be the traditional approach.  */
  180.           if (pzchat[1] == NULL || pzchat[1][0] != '-')
  181.         break;
  182.         }
  183.       else
  184.         {
  185.           int istr;
  186.  
  187.           istr = icexpect (qconn, cstrings, azstrings, aclens,
  188.                    qchat->uuconf_ctimeout,
  189.                    qchat->uuconf_fstrip);
  190.  
  191.           /* If we found the string, break out of the
  192.          subexpect/subsend loop.  */
  193.           if (istr == 0)
  194.         break;
  195.  
  196.           /* If we got an error, return FALSE.  */
  197.           if (istr < -1)
  198.         {
  199.           fret = FALSE;
  200.           break;
  201.         }
  202.  
  203.           /* If we found a failure string, log it and get out.  */
  204.           if (istr > 0)
  205.         {
  206.           ulog (LOG_ERROR, "Chat script failed: Got \"%s\"",
  207.             qchat->uuconf_pzfail[istr - 1]);
  208.           fret = FALSE;
  209.           break;
  210.         }
  211.  
  212.           /* We timed out; look for a send subsequence.  If none,
  213.          the chat script has failed.  */
  214.           if (pzchat[1] == NULL || pzchat[1][0] != '-')
  215.         {
  216.           ulog (LOG_ERROR, "Timed out in chat script");
  217.           fret = FALSE;
  218.           break;
  219.         }
  220.         }
  221.  
  222.       /* Send the send subsequence without the leading '-'.  A
  223.          \"\" will send nothing.  An empty string will send a
  224.          carriage return.  */
  225.       ++pzchat;
  226.       if (! fcsend (qconn, puuconf, *pzchat + 1, qsys, qdial, zphone,
  227.             ftranslate, qchat->uuconf_fstrip))
  228.         {
  229.           fret = FALSE;
  230.           break;
  231.         }
  232.  
  233.       /* If there is no expect subsequence, we are done.  */
  234.       if (pzchat[1] == NULL || pzchat[1][0] != '-')
  235.         break;
  236.  
  237.       /* Move on to next expect subsequence.  */
  238.       ++pzchat;
  239.     }
  240.  
  241.       if (! fret)
  242.     break;
  243.  
  244.       /* Move on to the send string.  If there is none, we have
  245.      succeeded.  */
  246.       do
  247.     {
  248.       ++pzchat;
  249.     }
  250.       while (*pzchat != NULL && (*pzchat)[0] == '-');
  251.  
  252.       if (*pzchat == NULL)
  253.     break;
  254.  
  255.       if (**pzchat != '\0')
  256.     {
  257.       if (! fcsend (qconn, puuconf, *pzchat, qsys, qdial, zphone,
  258.             ftranslate, qchat->uuconf_fstrip))
  259.         {
  260.           fret = FALSE;
  261.           break;
  262.         }
  263.     }
  264.  
  265.       ++pzchat;
  266.     }
  267.  
  268.   ubuffree (zbuf);
  269.   for (i = 1; i < cstrings; i++)
  270.     ubuffree (azstrings[i]);
  271.   xfree ((pointer) azstrings);
  272.   xfree ((pointer) aclens);
  273.  
  274.   return fret;
  275. }
  276.  
  277. /* Read characters and wait for one of a set of memory strings to come
  278.    in.  This returns the index into the array of the string that
  279.    arrives, or -1 on timeout, or -2 on error.  */
  280.  
  281. static int
  282. icexpect (qconn, cstrings, azstrings, aclens, ctimeout, fstrip)
  283.      struct sconnection *qconn;
  284.      int cstrings;
  285.      char **azstrings;
  286.      size_t *aclens;
  287.      int ctimeout;
  288.      boolean fstrip;
  289. {
  290.   int i;
  291.   size_t cmax;
  292.   char *zhave;
  293.   size_t chave;
  294.   long iendtime;
  295. #if DEBUG > 1
  296.   int cchars;
  297.   int iolddebug;
  298. #endif
  299.  
  300.   cmax = aclens[0];
  301.   for (i = 1; i < cstrings; i++)
  302.     if (cmax < aclens[i])
  303.       cmax = aclens[i];
  304.  
  305.   zhave = zbufalc (cmax);
  306.   chave = 0;
  307.  
  308.   iendtime = ixsysdep_time ((long *) NULL) + ctimeout;
  309.  
  310. #if DEBUG > 1
  311.   cchars = 0;
  312.   iolddebug = iDebug;
  313.   if (FDEBUGGING (DEBUG_CHAT))
  314.     {
  315.       udebug_buffer ("icexpect: Looking for", azstrings[0],
  316.              aclens[0]);
  317.       ulog (LOG_DEBUG_START, "icexpect: Got \"");
  318.       iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT);
  319.     }
  320. #endif
  321.  
  322.   while (TRUE)
  323.     {
  324.       int bchar;
  325.  
  326.       /* If we have no more time, get out.  */
  327.       if (ctimeout <= 0)
  328.     {
  329. #if DEBUG > 1
  330.       if (FDEBUGGING (DEBUG_CHAT))
  331.         {
  332.           ulog (LOG_DEBUG_END, "\" (timed out)");
  333.           iDebug = iolddebug;
  334.         }
  335. #endif
  336.       ubuffree (zhave);
  337.       return -1;
  338.     }
  339.  
  340.       /* Read one character at a time.  We could use a more complex
  341.      algorithm to read in larger batches, but it's probably not
  342.      worth it.  If the buffer is full, shift it left; we already
  343.      know that no string matches, and the buffer holds the largest
  344.      string, so this can't lose a match.  */
  345.       if (chave >= cmax)
  346.     {
  347.       size_t imove;
  348.  
  349.       for (imove = 0; imove < cmax - 1; imove++)
  350.         zhave[imove] = zhave[imove + 1];
  351.       --chave;
  352.     }
  353.  
  354.       /* The timeout/error return values from breceive_char are the
  355.      same as for this function.  */
  356.       bchar = breceive_char (qconn, ctimeout, TRUE);
  357.       if (bchar < 0)
  358.     {
  359. #if DEBUG > 1
  360.       if (FDEBUGGING (DEBUG_CHAT))
  361.         {
  362.           /* If there was an error, it will probably be logged in
  363.          the middle of our string, but this is only debugging
  364.          so it's not a big deal.  */
  365.           ulog (LOG_DEBUG_END, "\" (%s)",
  366.             bchar == -1 ? "timed out" : "error");
  367.           iDebug = iolddebug;
  368.         }
  369. #endif
  370.       ubuffree (zhave);
  371.       return bchar;
  372.     }
  373.  
  374.       /* Strip the parity bit if desired.  */
  375.       if (fstrip)
  376.     bchar &= 0x7f;
  377.  
  378.       zhave[chave] = (char) bchar;
  379.       ++chave;
  380.  
  381. #if DEBUG > 1
  382.       if (FDEBUGGING (DEBUG_CHAT))
  383.     {
  384.       char ab[5];
  385.  
  386.       ++cchars;
  387.       if (cchars > 60)
  388.         {
  389.           ulog (LOG_DEBUG_END, "\"");
  390.           ulog (LOG_DEBUG_START, "icexpect: Got \"");
  391.           cchars = 0;
  392.         }
  393.       (void) cdebug_char (ab, bchar);
  394.       ulog (LOG_DEBUG_CONTINUE, "%s", ab);
  395.     }
  396. #endif
  397.  
  398.       /* See if any of the strings can be found in the buffer.  Since
  399.      we read one character at a time, the string can only be found
  400.      at the end of the buffer.  */
  401.       for (i = 0; i < cstrings; i++)
  402.     {
  403.       if (aclens[i] <= chave
  404.           && memcmp (zhave + chave - aclens[i], azstrings[i],
  405.              aclens[i]) == 0)
  406.         {
  407. #if DEBUG > 1
  408.           if (FDEBUGGING (DEBUG_CHAT))
  409.         {
  410.           if (i == 0)
  411.             ulog (LOG_DEBUG_END, "\" (found it)");
  412.           else
  413.             {
  414.               ulog (LOG_DEBUG_END, "\"");
  415.               udebug_buffer ("icexpect: Found", azstrings[i],
  416.                      aclens[i]);
  417.             }
  418.           iDebug = iolddebug;
  419.         }
  420. #endif
  421.           ubuffree (zhave);
  422.           return i;
  423.         }
  424.     }
  425.  
  426.       ctimeout = (int) (iendtime - ixsysdep_time ((long *) NULL));
  427.     }
  428. }
  429.  
  430. #if DEBUG > 1
  431.  
  432. /* Debugging function for fcsend.  This takes the fquote variable, the
  433.    length of the string (0 if this an informational string which can
  434.    be printed directly) and the string itself.  It returns the new
  435.    value for fquote.  The fquote variable is TRUE if the debugging
  436.    output is in the middle of a quoted string.  */
  437.  
  438. static size_t cCsend_chars;
  439. static int iColddebug;
  440.  
  441. static boolean fcsend_debug P((boolean, size_t, const char *));
  442.  
  443. static boolean
  444. fcsend_debug (fquote, clen, zbuf)
  445.      boolean fquote;
  446.      size_t clen;
  447.      const char *zbuf;
  448. {
  449.   size_t cwas;
  450.  
  451.   if (! FDEBUGGING (DEBUG_CHAT))
  452.     return TRUE;
  453.  
  454.   cwas = cCsend_chars;
  455.   if (clen > 0)
  456.     cCsend_chars += clen;
  457.   else
  458.     cCsend_chars += strlen (zbuf);
  459.   if (cCsend_chars > 60 && cwas > 10)
  460.     {
  461.       ulog (LOG_DEBUG_END, "%s", fquote ? "\"" : "");
  462.       fquote = FALSE;
  463.       ulog (LOG_DEBUG_START, "fcsend: Writing");
  464.       cCsend_chars = 0;
  465.     }
  466.  
  467.   if (clen == 0)
  468.     {
  469.       ulog (LOG_DEBUG_CONTINUE, "%s %s", fquote ? "\"" : "", zbuf);
  470.       return FALSE;
  471.     }
  472.   else
  473.     {
  474.       int i;
  475.  
  476.       if (! fquote)
  477.     ulog (LOG_DEBUG_CONTINUE, " \"");
  478.       for (i = 0; i < clen; i++)
  479.     {
  480.       char ab[5];
  481.  
  482.       (void) cdebug_char (ab, zbuf[i]);
  483.       ulog (LOG_DEBUG_CONTINUE, "%s", ab);
  484.     }
  485.  
  486.       return TRUE;
  487.     }
  488. }
  489.  
  490. /* Finish up the debugging information for fcsend.  */
  491.  
  492. static void ucsend_debug_end P((boolean, boolean));
  493.  
  494. static void
  495. ucsend_debug_end (fquote, ferr)
  496.      boolean fquote;
  497.      boolean ferr;
  498. {
  499.   if (! FDEBUGGING (DEBUG_CHAT))
  500.     return;
  501.  
  502.   if (fquote)
  503.     ulog (LOG_DEBUG_CONTINUE, "\"");
  504.  
  505.   if (ferr)
  506.     ulog (LOG_DEBUG_CONTINUE, " (error)");
  507.  
  508.   ulog (LOG_DEBUG_END, "%s", "");
  509.  
  510.   iDebug = iColddebug;
  511. }
  512.  
  513. #else /* DEBUG <= 1 */
  514.  
  515. /* Use macro definitions to make fcsend look neater.  */
  516.  
  517. #define fcsend_debug(fquote, clen, zbuf) TRUE
  518.  
  519. #define ucsend_debug_end(fquote, ferror)
  520.  
  521. #endif /* DEBUG <= 1 */
  522.  
  523. /* Send a string out.  This has to parse escape sequences as it goes.
  524.    Note that it handles the dialer escape sequences (\e, \E, \D, \T)
  525.    although they make no sense for chatting with a system.  */
  526.  
  527. static boolean
  528. fcsend (qconn, puuconf, z, qsys, qdial, zphone, ftranslate, fstrip)
  529.      struct sconnection *qconn;
  530.      pointer puuconf;
  531.      const char *z;
  532.      const struct uuconf_system *qsys;
  533.      const struct uuconf_dialer *qdial;
  534.      const char *zphone;
  535.      boolean ftranslate;
  536.      boolean fstrip;
  537. {
  538.   boolean fnocr;
  539.   boolean (*pfwrite) P((struct sconnection *, const char *, size_t));
  540.   char *zcallout_login;
  541.   char *zcallout_pass;
  542.   boolean fquote;
  543.  
  544.   if (strcmp (z, "\"\"") == 0)
  545.     return TRUE;
  546.  
  547.   fnocr = FALSE;
  548.   pfwrite = fconn_write;
  549.   zcallout_login = NULL;
  550.   zcallout_pass = NULL;
  551.  
  552. #if DEBUG > 1
  553.   if (FDEBUGGING (DEBUG_CHAT))
  554.     {
  555.       ulog (LOG_DEBUG_START, "fcsend: Writing");
  556.       fquote = FALSE;
  557.       cCsend_chars = 0;
  558.       iColddebug = iDebug;
  559.       iDebug &=~ (DEBUG_OUTGOING | DEBUG_PORT);
  560.     }
  561. #endif
  562.  
  563.   while (*z != '\0')
  564.     {
  565.       const char *zlook;
  566.       boolean fsend;
  567.       char bsend;
  568.  
  569.       zlook = z + strcspn ((char *) z, "\\BE");
  570.  
  571.       if (zlook > z)
  572.     {
  573.       size_t c;
  574.  
  575.       c = zlook - z;
  576.       fquote = fcsend_debug (fquote, c, z);
  577.       if (! (*pfwrite) (qconn, z, c))
  578.         {
  579.           ucsend_debug_end (fquote, TRUE);
  580.           return FALSE;
  581.         }
  582.     }
  583.  
  584.       if (*zlook == '\0')
  585.     break;
  586.  
  587.       z = zlook;
  588.  
  589.       fsend = FALSE;
  590.       switch (*z)
  591.     {
  592.     case 'B':
  593.       if (strncmp (z, "BREAK", 5) == 0)
  594.         {
  595.           fquote = fcsend_debug (fquote, (size_t) 0, "break");
  596.           if (! fconn_break (qconn))
  597.         {
  598.           ucsend_debug_end (fquote, TRUE);
  599.           return FALSE;
  600.         }
  601.           fnocr = TRUE;
  602.           z += 5;
  603.         }
  604.       else
  605.         {
  606.           fsend = TRUE;
  607.           bsend = 'B';
  608.           ++z;
  609.         }
  610.       break;
  611.     case 'E':
  612.       if (strncmp (z, "EOT", 3) == 0)
  613.         {
  614.           fsend = TRUE;
  615.           bsend = '\004';
  616.           fnocr = TRUE;
  617.           z += 3;
  618.         }
  619.       else
  620.         {
  621.           fsend = TRUE;
  622.           bsend = 'E';
  623.           ++z;
  624.         }
  625.       break;
  626.     case '\\':
  627.       ++z;
  628.       switch (*z)
  629.         {
  630.         case '-':
  631.           fsend = TRUE;
  632.           bsend = '-';
  633.           break;
  634.         case 'b':
  635.           fsend = TRUE;
  636.           bsend = '\b';
  637.           break;
  638.         case 'c':
  639.           fnocr = TRUE;
  640.           break;
  641.         case 'd':
  642.           fquote = fcsend_debug (fquote, (size_t) 0, "sleep");
  643.           usysdep_sleep (2);
  644.           break;
  645.         case 'e':
  646.           fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-off");
  647.           pfwrite = fconn_write;
  648.           break;
  649.         case 'E':
  650.           fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-on");
  651.           if (fstrip)
  652.         pfwrite = fcecho_send_strip;
  653.           else
  654.         pfwrite = fcecho_send_nostrip;
  655.           break;
  656.         case 'K':
  657.           fquote = fcsend_debug (fquote, (size_t) 0, "break");
  658.           if (! fconn_break (qconn))
  659.         {
  660.           ucsend_debug_end (fquote, TRUE);
  661.           return FALSE;
  662.         }
  663.           break;
  664.         case 'n':
  665.           fsend = TRUE;
  666.           bsend = '\n';
  667.           break;
  668.         case 'N':
  669.           fsend = TRUE;
  670.           bsend = '\0';
  671.           break;
  672.         case 'p':
  673.           fquote = fcsend_debug (fquote, (size_t) 0, "pause");
  674.           usysdep_pause ();
  675.           break;
  676.         case 'r':
  677.           fsend = TRUE;
  678.           bsend = '\r';
  679.           break;
  680.         case 's':
  681.           fsend = TRUE;
  682.           bsend = ' ';
  683.           break;
  684.         case 't':
  685.           fsend = TRUE;
  686.           bsend = '\t';
  687.           break;
  688.         case '\0':
  689.           --z;
  690.           /* Fall through.  */
  691.         case '\\':
  692.           fsend = TRUE;
  693.           bsend = '\\';
  694.           break;
  695.         case '0': case '1': case '2': case '3': case '4':
  696.         case '5': case '6': case '7': case '8': case '9':
  697.           fsend = TRUE;
  698.           bsend = *z - '0';
  699.           if (z[1] >= '0' && z[1] <= '7')
  700.         bsend = (char) (8 * bsend + *++z - '0');
  701.           if (z[1] >= '0' && z[1] <= '7')
  702.         bsend = (char) (8 * bsend + *++z - '0');
  703.           break;
  704.         case 'x':
  705.           fsend = TRUE;
  706.           bsend = 0;
  707.           while (isxdigit (BUCHAR (z[1])))
  708.         {
  709.           if (isdigit (BUCHAR (z[1])))
  710.             bsend = (char) (16 * bsend + *++z - '0');
  711.           else if (isupper (BUCHAR (z[1])))
  712.             bsend = (char) (16 * bsend + *++z - 'A');
  713.           else
  714.             bsend = (char) (16 * bsend + *++z - 'a');
  715.         }
  716.           break;
  717.         case 'L':
  718.           {
  719.         const char *zlog;
  720.  
  721.         if (qsys == NULL)
  722.           {
  723.             ucsend_debug_end (fquote, TRUE);
  724.             ulog (LOG_ERROR, "Illegal use of \\L");
  725.             return FALSE;
  726.           }
  727.         zlog = qsys->uuconf_zcall_login;
  728.         if (zlog == NULL)
  729.           {
  730.             ucsend_debug_end (fquote, TRUE);
  731.             ulog (LOG_ERROR, "No login defined");
  732.             return FALSE;
  733.           }
  734.         if (zlog[0] == '*' && zlog[1] == '\0')
  735.           {
  736.             if (zcallout_login == NULL)
  737.               {
  738.             int iuuconf;
  739.  
  740.             iuuconf = uuconf_callout (puuconf, qsys,
  741.                           &zcallout_login,
  742.                           &zcallout_pass);
  743.             if (iuuconf == UUCONF_NOT_FOUND
  744.                 || zcallout_login == NULL)
  745.               {
  746.                 ucsend_debug_end (fquote, TRUE);
  747.                 ulog (LOG_ERROR, "No login defined");
  748.                 return FALSE;
  749.               }
  750.             else if (iuuconf != UUCONF_SUCCESS)
  751.               {
  752.                 ucsend_debug_end (fquote, TRUE);
  753.                 ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
  754.                 return FALSE;
  755.               }
  756.               }
  757.             zlog = zcallout_login;
  758.           }
  759.         fquote = fcsend_debug (fquote, (size_t) 0, "login");
  760.         fquote = fcsend_debug (fquote, strlen (zlog), zlog);
  761.         if (! (*pfwrite) (qconn, zlog, strlen (zlog)))
  762.           {
  763.             ucsend_debug_end (fquote, TRUE);
  764.             return FALSE;
  765.           }
  766.           }
  767.           break;
  768.         case 'P':
  769.           {
  770.         const char *zpass;
  771.  
  772.         if (qsys == NULL)
  773.           {
  774.             ucsend_debug_end (fquote, TRUE);
  775.             ulog (LOG_ERROR, "Illegal use of \\P");
  776.             return FALSE;
  777.           }
  778.         zpass = qsys->uuconf_zcall_password;
  779.         if (zpass == NULL)
  780.           {
  781.             ucsend_debug_end (fquote, TRUE);
  782.             ulog (LOG_ERROR, "No password defined");
  783.             return FALSE;
  784.           }
  785.         if (zpass[0] == '*' && zpass[1] == '\0')
  786.           {
  787.             if (zcallout_pass == NULL)
  788.               {
  789.             int iuuconf;
  790.  
  791.             iuuconf = uuconf_callout (puuconf, qsys,
  792.                           &zcallout_login,
  793.                           &zcallout_pass);
  794.             if (iuuconf == UUCONF_NOT_FOUND
  795.                 || zcallout_pass == NULL)
  796.               {
  797.                 ucsend_debug_end (fquote, TRUE);
  798.                 ulog (LOG_ERROR, "No password defined");
  799.                 return FALSE;
  800.               }
  801.             else if (iuuconf != UUCONF_SUCCESS)
  802.               {
  803.                 ucsend_debug_end (fquote, TRUE);
  804.                 ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
  805.                 return FALSE;
  806.               }
  807.               }
  808.             zpass = zcallout_pass;
  809.           }
  810.         fquote = fcsend_debug (fquote, (size_t) 0, "password");
  811.         fquote = fcsend_debug (fquote, strlen (zpass), zpass);
  812.         if (! (*pfwrite) (qconn, zpass, strlen (zpass)))
  813.           {
  814.             ucsend_debug_end (fquote, TRUE);
  815.             return FALSE;
  816.           }
  817.           }
  818.           break;
  819.         case 'D':
  820.           if (qdial == NULL || zphone == NULL)
  821.         {
  822.           ucsend_debug_end (fquote, TRUE);
  823.           ulog (LOG_ERROR, "Illegal use of \\D");
  824.           return FALSE;
  825.         }
  826.           fquote = fcsend_debug (fquote, (size_t) 0, "\\D");
  827.           if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite,
  828.                  ftranslate, &fquote))
  829.         {
  830.           ucsend_debug_end (fquote, TRUE);
  831.           return FALSE;
  832.         }
  833.           break;
  834.         case 'T':
  835.           if (qdial == NULL || zphone == NULL)
  836.         {
  837.           ucsend_debug_end (fquote, TRUE);
  838.           ulog (LOG_ERROR, "Illegal use of \\T");
  839.           return FALSE;
  840.         }
  841.           fquote = fcsend_debug (fquote, (size_t) 0, "\\T");
  842.           if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite, TRUE,
  843.                  &fquote))
  844.         {
  845.           ucsend_debug_end (fquote, TRUE);
  846.           return FALSE;
  847.         }
  848.           break;
  849.         case 'M':
  850.           if (qdial == NULL)
  851.         {
  852.           ucsend_debug_end (fquote, TRUE);
  853.           ulog (LOG_ERROR, "Illegal use of \\M");
  854.           return FALSE;
  855.         }
  856.           fquote = fcsend_debug (fquote, (size_t) 0, "ignore-carrier");
  857.           if (! fconn_carrier (qconn, FALSE))
  858.         {
  859.           ucsend_debug_end (fquote, TRUE);
  860.           return FALSE;
  861.         }
  862.           break;
  863.         case 'm':
  864.           if (qdial == NULL)
  865.         {
  866.           ucsend_debug_end (fquote, TRUE);
  867.           ulog (LOG_ERROR, "Illegal use of \\m");
  868.           return FALSE;
  869.         }
  870.           if (qdial->uuconf_fcarrier)
  871.         {
  872.           fquote = fcsend_debug (fquote, (size_t) 0, "need-carrier");
  873.           if (! fconn_carrier (qconn, TRUE))
  874.             {
  875.               ucsend_debug_end (fquote, TRUE);
  876.               return FALSE;
  877.             }
  878.         }
  879.           break;
  880.         default:
  881.           /* This error message will screw up any debugging
  882.          information, but it's easily avoidable.  */
  883.           ulog (LOG_ERROR,
  884.             "Unrecognized escape sequence \\%c in send string",
  885.             *z);
  886.           fsend = TRUE;
  887.           bsend = *z;
  888.           break;
  889.         }
  890.       ++z;
  891.       break;
  892. #if DEBUG > 0
  893.     default:
  894.       ulog (LOG_FATAL, "fcsend: Can't happen");
  895.       break;
  896. #endif
  897.     }
  898.       
  899.       if (fsend)
  900.     {
  901.       fquote = fcsend_debug (fquote, (size_t) 1, &bsend);
  902.       if (! (*pfwrite) (qconn, &bsend, (size_t) 1))
  903.         {
  904.           ucsend_debug_end (fquote, TRUE);
  905.           return FALSE;
  906.         }
  907.     }
  908.     }
  909.  
  910.   xfree ((pointer) zcallout_login);
  911.   xfree ((pointer) zcallout_pass);
  912.  
  913.   /* Output a final carriage return, unless there was a \c.  Don't
  914.      bother to check for an echo.  */
  915.   if (! fnocr)
  916.     {
  917.       char b;
  918.  
  919.       b = '\r';
  920.       fquote = fcsend_debug (fquote, (size_t) 1, &b);
  921.       if (! fconn_write (qconn, &b, (size_t) 1))
  922.     {
  923.       ucsend_debug_end (fquote, TRUE);
  924.       return FALSE;
  925.     }
  926.     }
  927.  
  928.   ucsend_debug_end (fquote, FALSE);
  929.  
  930.   return TRUE;
  931. }
  932.  
  933. /* Write out a phone number with optional dialcode translation.  The
  934.    pfquote argument is only used for debugging.  */
  935.  
  936. static boolean
  937. fcphone (qconn, puuconf, qdial, zphone, pfwrite, ftranslate, pfquote)
  938.      struct sconnection *qconn;
  939.      pointer puuconf;
  940.      const struct uuconf_dialer *qdial;
  941.      const char *zphone;
  942.      boolean (*pfwrite) P((struct sconnection *qc, const char *zwrite,
  943.                size_t cwrite));
  944.      boolean ftranslate;
  945.      boolean *pfquote;
  946. {
  947.   const char *zprefix, *zsuffix;
  948.  
  949.   if (ftranslate)
  950.     {
  951.       if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix))
  952.     return FALSE;
  953.     }
  954.   else
  955.     {
  956.       zprefix = zphone;
  957.       zsuffix = NULL;
  958.     }
  959.  
  960.   while (zprefix != NULL)
  961.     {
  962.       while (TRUE)
  963.     {
  964.       const char *z;
  965.       const char *zstr;
  966.  
  967.       z = zprefix + strcspn ((char *) zprefix, "=-");
  968.       if (z > zprefix)
  969.         {
  970.           size_t clen;
  971.  
  972.           clen = z - zprefix;
  973.           *pfquote = fcsend_debug (*pfquote, clen, zprefix);
  974.           if (! (*pfwrite) (qconn, zprefix, clen))
  975.         return FALSE;
  976.         }
  977.  
  978.       if (*z == '=')
  979.         zstr = qdial->uuconf_zdialtone;
  980.       else if (*z == '-')
  981.         zstr = qdial->uuconf_zpause;
  982.       else            /* *z == '\0' */
  983.         break;
  984.  
  985.       if (zstr != NULL)
  986.         {
  987.           *pfquote = fcsend_debug (*pfquote, strlen (zstr), zstr);
  988.           if (! (*pfwrite) (qconn, zstr, strlen (zstr)))
  989.         return FALSE;
  990.         }
  991.  
  992.       zprefix = z + 1;
  993.     }
  994.  
  995.       zprefix = zsuffix;
  996.       zsuffix = NULL;
  997.     }
  998.  
  999.   return TRUE;
  1000. }
  1001.  
  1002. /* Given a phone number, run it through dial code translation
  1003.    returning two strings.  */
  1004.  
  1005. static boolean
  1006. fctranslate (puuconf, zphone, pzprefix, pzsuffix)
  1007.      pointer puuconf;
  1008.      const char *zphone;
  1009.      const char **pzprefix;
  1010.      const char **pzsuffix;
  1011. {
  1012.   int iuuconf;
  1013.   char *zdialcode, *zto;
  1014.   const char *zfrom;
  1015.   char *ztrans;
  1016.  
  1017.   *pzprefix = zphone;
  1018.   *pzsuffix = NULL;
  1019.  
  1020.   zdialcode = zbufalc (strlen (zphone) + 1);
  1021.   zfrom = zphone;
  1022.   zto = zdialcode;
  1023.   while (*zfrom != '\0' && isalpha (BUCHAR (*zfrom)))
  1024.     *zto++ = *zfrom++;
  1025.   *zto = '\0';
  1026.  
  1027.   if (*zdialcode == '\0')
  1028.     {
  1029.       ubuffree (zdialcode);
  1030.       return TRUE;
  1031.     }
  1032.  
  1033.   iuuconf = uuconf_dialcode (puuconf, zdialcode, &ztrans);
  1034.  
  1035.   ubuffree (zdialcode);
  1036.  
  1037.   if (iuuconf == UUCONF_NOT_FOUND)
  1038.     return TRUE;
  1039.   else if (iuuconf != UUCONF_SUCCESS)
  1040.     {
  1041.       ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
  1042.       return FALSE;
  1043.     }
  1044.   else
  1045.     {
  1046.       /* We really should figure out a way to free up ztrans here.  */
  1047.       *pzprefix = ztrans;
  1048.       *pzsuffix = zfrom;
  1049.       return TRUE;
  1050.     }
  1051. }
  1052.  
  1053. /* Write out a string making sure the each character is echoed back.
  1054.    There are two versions of this function, one which strips the
  1055.    parity bit from the characters and one which does not.  This is so
  1056.    that I can use a single function pointer in fcsend, and to avoid
  1057.    using any static variables so that I can put chat scripts in a
  1058.    library some day.  */
  1059.  
  1060. static boolean
  1061. fcecho_send_strip (qconn, zwrite, cwrite)
  1062.      struct sconnection *qconn;
  1063.      const char *zwrite;
  1064.      size_t cwrite;
  1065. {
  1066.   return fcecho_send (qconn, zwrite, cwrite, TRUE);
  1067. }
  1068.  
  1069. static boolean
  1070. fcecho_send_nostrip (qconn, zwrite, cwrite)
  1071.      struct sconnection *qconn;
  1072.      const char *zwrite;
  1073.      size_t cwrite;
  1074. {
  1075.   return fcecho_send (qconn, zwrite, cwrite, FALSE);
  1076. }
  1077.  
  1078. static boolean
  1079. fcecho_send (qconn, zwrite, cwrite, fstrip)
  1080.      struct sconnection *qconn;
  1081.      const char *zwrite;
  1082.      size_t cwrite;
  1083.      boolean fstrip;
  1084. {
  1085.   const char *zend;
  1086.  
  1087.   zend = zwrite + cwrite;
  1088.  
  1089.   for (; zwrite < zend; zwrite++)
  1090.     {
  1091.       int b;
  1092.       char bwrite;
  1093.  
  1094.       bwrite = *zwrite;
  1095.       if (! fconn_write (qconn, &bwrite, (size_t) 1))
  1096.     return FALSE;
  1097.       if (fstrip)
  1098.     bwrite &= 0x7f;
  1099.       do
  1100.     {
  1101.       /* We arbitrarily wait five seconds for the echo.  */
  1102.       b = breceive_char (qconn, 5, TRUE);
  1103.       /* Now b == -1 on timeout, -2 on error.  */
  1104.       if (b < 0)
  1105.         {
  1106.           if (b == -1)
  1107.         ulog (LOG_ERROR, "Character not echoed");
  1108.           return FALSE;
  1109.         }
  1110.       if (fstrip)
  1111.         b &= 0x7f;
  1112.     }
  1113.       while (b != BUCHAR (bwrite));
  1114.     }
  1115.  
  1116.   return TRUE;
  1117. }
  1118.  
  1119. /* Run a chat program.  Expand any escape sequences and call a system
  1120.    dependent program to run it.  */
  1121.  
  1122. static boolean
  1123. fcprogram (qconn, puuconf, pzprogram, qsys, qdial, zphone, zport, ibaud)
  1124.      struct sconnection *qconn;
  1125.      pointer puuconf;
  1126.      char **pzprogram;
  1127.      const struct uuconf_system *qsys;
  1128.      const struct uuconf_dialer *qdial;
  1129.      const char *zphone;
  1130.      const char *zport;
  1131.      long ibaud;
  1132. {
  1133.   size_t cargs;
  1134.   char **pzpass, **pzarg;
  1135.   char **pz;
  1136.   char *zcallout_login;
  1137.   char *zcallout_pass;
  1138.   boolean fret;
  1139.  
  1140.   cargs = 1;
  1141.   for (pz = pzprogram; *pz != NULL; pz++)
  1142.     ++cargs;
  1143.  
  1144.   pzpass = (char **) xmalloc (cargs * sizeof (char *));
  1145.  
  1146.   zcallout_login = NULL;
  1147.   zcallout_pass = NULL;
  1148.   fret = TRUE;
  1149.  
  1150.   /* Copy the string into memory expanding escape sequences.  */
  1151.   for (pz = pzprogram, pzarg = pzpass; *pz != NULL; pz++, pzarg++)
  1152.     {
  1153.       const char *zfrom;
  1154.       size_t calc, clen;
  1155.       char *zto;
  1156.  
  1157.       if (strchr (*pz, '\\') == NULL)
  1158.     {
  1159.       *pzarg = zbufcpy (*pz);
  1160.       continue;
  1161.     }
  1162.       
  1163.       *pzarg = NULL;
  1164.       zto = NULL;
  1165.       calc = 0;
  1166.       clen = 0;
  1167.  
  1168.       for (zfrom = *pz; *zfrom != '\0'; zfrom++)
  1169.     {
  1170.       const char *zadd = NULL;
  1171.       size_t cadd;
  1172.       char abadd[15];
  1173.  
  1174.       if (*zfrom != '\\')
  1175.         {
  1176.           if (clen + 2 > calc)
  1177.         {
  1178.           char *znew;
  1179.  
  1180.           calc = clen + 50;
  1181.           znew = zbufalc (calc);
  1182.           memcpy (znew, *pzarg, clen);
  1183.           ubuffree (*pzarg);
  1184.           *pzarg = znew;
  1185.           zto = znew + clen;
  1186.         }
  1187.           *zto++ = *zfrom;
  1188.           ++clen;
  1189.           continue;
  1190.         }
  1191.  
  1192.       ++zfrom;
  1193.       switch (*zfrom)
  1194.         {
  1195.         case '\0':
  1196.           --zfrom;
  1197.           /* Fall through.  */
  1198.         case '\\':
  1199.           zadd = "\\";
  1200.           break;
  1201.         case 'L':
  1202.           {
  1203.         const char *zlog;
  1204.  
  1205.         if (qsys == NULL)
  1206.           {
  1207.             ulog (LOG_ERROR, "chat-program: Illegal use of \\L");
  1208.             fret = FALSE;
  1209.             break;
  1210.           }
  1211.         zlog = qsys->uuconf_zcall_login;
  1212.         if (zlog == NULL)
  1213.           {
  1214.             ulog (LOG_ERROR, "chat-program: No login defined");
  1215.             fret = FALSE;
  1216.             break;
  1217.           }
  1218.         if (zlog[0] == '*' && zlog[1] == '\0')
  1219.           {
  1220.             if (zcallout_login == NULL)
  1221.               {
  1222.             int iuuconf;
  1223.  
  1224.             iuuconf = uuconf_callout (puuconf, qsys,
  1225.                           &zcallout_login,
  1226.                           &zcallout_pass);
  1227.             if (iuuconf == UUCONF_NOT_FOUND
  1228.                 || zcallout_login == NULL)
  1229.               {
  1230.                 ulog (LOG_ERROR,
  1231.                   "chat-program: No login defined");
  1232.                 fret = FALSE;
  1233.                 break;
  1234.               }
  1235.             else if (iuuconf != UUCONF_SUCCESS)
  1236.               {
  1237.                 ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
  1238.                 fret = FALSE;
  1239.                 break;
  1240.               }
  1241.               }
  1242.             zlog = zcallout_login;
  1243.           }
  1244.         zadd = zlog;
  1245.           }
  1246.           break;
  1247.         case 'P':
  1248.           {
  1249.         const char *zpass;
  1250.  
  1251.         if (qsys == NULL)
  1252.           {
  1253.             ulog (LOG_ERROR, "chat-program: Illegal use of \\P");
  1254.             fret = FALSE;
  1255.             break;
  1256.           }
  1257.         zpass = qsys->uuconf_zcall_password;
  1258.         if (zpass == NULL)
  1259.           {
  1260.             ulog (LOG_ERROR, "chat-program: No password defined");
  1261.             fret = FALSE;
  1262.             break;
  1263.           }
  1264.         if (zpass[0] == '*' && zpass[1] == '\0')
  1265.           {
  1266.             if (zcallout_pass == NULL)
  1267.               {
  1268.             int iuuconf;
  1269.  
  1270.             iuuconf = uuconf_callout (puuconf, qsys,
  1271.                           &zcallout_login,
  1272.                           &zcallout_pass);
  1273.             if (iuuconf == UUCONF_NOT_FOUND
  1274.                 || zcallout_pass == NULL)
  1275.               {
  1276.                 ulog (LOG_ERROR,
  1277.                   "chat-program: No password defined");
  1278.                 fret = FALSE;
  1279.                 break;
  1280.               }
  1281.             else if (iuuconf != UUCONF_SUCCESS)
  1282.               {
  1283.                 ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
  1284.                 fret = FALSE;
  1285.                 break;
  1286.               }
  1287.               }
  1288.             zpass = zcallout_pass;
  1289.           }
  1290.         zadd = zpass;
  1291.           }
  1292.           break;
  1293.         case 'D':
  1294.           if (qdial == NULL || zphone == NULL)
  1295.         {
  1296.           ulog (LOG_ERROR, "chat-program: Illegal use of \\D");
  1297.           fret = FALSE;
  1298.           break;
  1299.         }
  1300.           zadd = zphone;
  1301.           break;
  1302.         case 'T':
  1303.           {
  1304.         const char *zprefix, *zsuffix;
  1305.  
  1306.         if (qdial == NULL || zphone == NULL)
  1307.           {
  1308.             ulog (LOG_ERROR, "chat-program: Illegal use of \\T");
  1309.             fret = FALSE;
  1310.             break;
  1311.           }
  1312.  
  1313.         if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix))
  1314.           {
  1315.             fret = FALSE;
  1316.             break;
  1317.           }
  1318.  
  1319.         if (zsuffix == NULL)
  1320.           zadd = zprefix;
  1321.         else
  1322.           {
  1323.             size_t cprefix;
  1324.  
  1325.             cprefix = strlen (zprefix);
  1326.             if (clen + cprefix + 1 > calc)
  1327.               {
  1328.             char *znew;
  1329.  
  1330.             calc = clen + cprefix + 20;
  1331.             znew = zbufalc (calc);
  1332.             memcpy (znew, *pzarg, clen);
  1333.             ubuffree (*pzarg);
  1334.             *pzarg = znew;
  1335.             zto = znew + clen;
  1336.               }
  1337.             memcpy (zto, zprefix, cprefix);
  1338.             zto += cprefix;
  1339.             clen += cprefix;
  1340.             zadd = zsuffix;
  1341.           }
  1342.           }
  1343.           break;
  1344.         case 'Y':
  1345.           if (zLdevice == NULL && zport == NULL)
  1346.         {
  1347.           ulog (LOG_ERROR, "chat-program: Illegal use of \\Y");
  1348.           fret = FALSE;
  1349.           break;
  1350.         }
  1351.           /* zLdevice will generally make more sense than zport, but
  1352.          it might not be set yet.  */
  1353.           zadd = zLdevice;
  1354.           if (zadd == NULL)
  1355.         zadd = zport;
  1356.           break;
  1357.         case 'Z':
  1358.           if (qsys == NULL)
  1359.         {
  1360.           ulog (LOG_ERROR, "chat-program: Illegal use of \\Z");
  1361.           fret = FALSE;
  1362.           break;
  1363.         }
  1364.           zadd = qsys->uuconf_zname;
  1365.           break;
  1366.         case 'S':
  1367.           {
  1368.         if (ibaud == 0)
  1369.           {
  1370.             ulog (LOG_ERROR, "chat-program: Illegal use of \\S");
  1371.             fret = FALSE;
  1372.             break;
  1373.           }
  1374.         sprintf (abadd, "%ld", ibaud);
  1375.         zadd = abadd;
  1376.           }
  1377.           break;
  1378.         default:
  1379.           {
  1380.         ulog (LOG_ERROR,
  1381.               "chat-program: Unrecognized escape sequence \\%c",
  1382.               *zfrom);
  1383.         abadd[0] = *zfrom;
  1384.         abadd[1] = '\0';
  1385.         zadd = abadd;
  1386.           }
  1387.           break;
  1388.         }
  1389.  
  1390.       if (! fret)
  1391.         break;
  1392.  
  1393.       cadd = strlen (zadd);
  1394.       if (clen + cadd + 1 > calc)
  1395.         {
  1396.           char *znew;
  1397.  
  1398.           calc = clen + cadd + 20;
  1399.           znew = zbufalc (calc);
  1400.           memcpy (znew, *pzarg, clen);
  1401.           ubuffree (*pzarg);
  1402.           *pzarg = znew;
  1403.           zto = znew + clen;
  1404.         }
  1405.       memcpy (zto, zadd, cadd + 1);
  1406.       zto += cadd;
  1407.       clen += cadd;
  1408.     }
  1409.  
  1410.       if (! fret)
  1411.     break;
  1412.  
  1413.       *zto++ = '\0';
  1414.       ++clen;
  1415.     }
  1416.  
  1417.   *pzarg = NULL;
  1418.  
  1419.   if (fret)
  1420.     fret = fconn_run_chat (qconn, pzpass);
  1421.  
  1422.   for (pz = pzpass; *pz != NULL; pz++)
  1423.     ubuffree (*pz);
  1424.   xfree ((pointer) pzpass);
  1425.   xfree ((pointer) zcallout_login);
  1426.   xfree ((pointer) zcallout_pass);
  1427.  
  1428.   return fret;
  1429. }
  1430.